// Copyright (c) MatrixFramework. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Matrix.ServiceCommunication.Options;
using Matrix.ServiceCommunication.Resilience;

namespace Matrix.ServiceCommunication.Services;

/// <summary>
/// 服务客户端工厂实现
/// </summary>
public class ServiceClientFactory : IServiceClientFactory
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IOptionsMonitor<ServiceCommunicationOptions> _options;
    private readonly ILogger<ServiceClientFactory> _logger;
    private readonly IMemoryCache _cache;
    private readonly IServiceDiscoveryProvider _serviceDiscoveryProvider;
    private readonly IAuthenticationProvider _authenticationProvider;
    private readonly IResilienceStrategyProvider _resilienceStrategyProvider;

    public ServiceClientFactory(
        IHttpClientFactory httpClientFactory,
        IOptionsMonitor<ServiceCommunicationOptions> options,
        ILogger<ServiceClientFactory> logger,
        IMemoryCache cache,
        IServiceDiscoveryProvider serviceDiscoveryProvider,
        IAuthenticationProvider authenticationProvider,
        IResilienceStrategyProvider resilienceStrategyProvider)
    {
        _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
        _options = options ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
        _serviceDiscoveryProvider = serviceDiscoveryProvider ?? throw new ArgumentNullException(nameof(serviceDiscoveryProvider));
        _authenticationProvider = authenticationProvider ?? throw new ArgumentNullException(nameof(authenticationProvider));
        _resilienceStrategyProvider = resilienceStrategyProvider ?? throw new ArgumentNullException(nameof(resilienceStrategyProvider));
    }

    public HttpClient CreateClient(string serviceName)
    {
        var options = _options.CurrentValue;

        // 优先使用带容错的客户端
        if (options.Resilience != null)
        {
            try
            {
                var resilientClient = _httpClientFactory.CreateClient($"Resilient-{serviceName}");
                ConfigureClient(resilientClient, serviceName);
                _logger.LogDebug("使用带容错的HTTP客户端: {ServiceName}", serviceName);
                return resilientClient;
            }
            catch (InvalidOperationException)
            {
                _logger.LogDebug("容错客户端不可用，使用标准客户端: {ServiceName}", serviceName);
            }
        }

        // 回退到标准客户端
        var client = _httpClientFactory.CreateClient($"Service-{serviceName}");
        ConfigureClient(client, serviceName);
        return client;
    }

    public async Task<HttpClient> CreateClientAsync(string serviceName, UserContext? userContext = null, CancellationToken cancellationToken = default)
    {
        var options = _options.CurrentValue;

        // 优先使用带容错的客户端
        if (options.Resilience != null)
        {
            try
            {
                var resilientClient = _httpClientFactory.CreateClient($"Resilient-{serviceName}");

                // 配置基础设置
                await ConfigureClientAsync(resilientClient, serviceName, userContext, cancellationToken);

                _logger.LogDebug("使用带容错的异步HTTP客户端: {ServiceName}", serviceName);
                return resilientClient;
            }
            catch (InvalidOperationException)
            {
                _logger.LogDebug("容错客户端不可用，使用标准异步客户端: {ServiceName}", serviceName);
            }
        }

        // 回退到标准客户端
        var client = _httpClientFactory.CreateClient($"Service-{serviceName}");

        // 配置基础设置
        await ConfigureClientAsync(client, serviceName, userContext, cancellationToken);

        return client;
    }

    public async Task<Uri> GetServiceAddressAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        var cacheKey = $"service_address:{serviceName}";

        if (_cache.TryGetValue(cacheKey, out Uri? cachedAddress) && cachedAddress != null)
        {
            return cachedAddress;
        }

        var options = _options.CurrentValue;
        var serviceAddress = await _serviceDiscoveryProvider.GetServiceAddressAsync(serviceName, cancellationToken);

        // 缓存服务地址
        var cacheOptions = new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(options.ServiceDiscovery.CacheSeconds)
        };
        _cache.Set(cacheKey, serviceAddress, cacheOptions);

        _logger.LogDebug("获取服务地址: {ServiceName} -> {ServiceAddress}", serviceName, serviceAddress);

        return serviceAddress;
    }

    private void ConfigureClient(HttpClient client, string serviceName)
    {
        var options = _options.CurrentValue;

        // 设置超时时间
        client.Timeout = TimeSpan.FromSeconds(options.DefaultTimeoutSeconds);

        // 设置默认请求头
        client.DefaultRequestHeaders.Add("User-Agent", "Matrix.ServiceCommunication/1.0");
        client.DefaultRequestHeaders.Add("X-Service-Name", serviceName);

        // 设置API版本
        client.DefaultRequestHeaders.Add("X-API-Version", "v1");
    }

    private async Task ConfigureClientAsync(HttpClient client, string serviceName, UserContext? userContext, CancellationToken cancellationToken)
    {
        var options = _options.CurrentValue;

        // 配置基础设置
        ConfigureClient(client, serviceName);

        // 获取服务地址
        var serviceAddress = await GetServiceAddressAsync(serviceName, cancellationToken);
        client.BaseAddress = serviceAddress;

        // 添加认证信息
        await AddAuthenticationAsync(client, serviceName, cancellationToken);

        // 添加用户上下文信息
        if (userContext != null)
        {
            AddUserContextHeaders(client, userContext);
        }
    }

    private async Task AddAuthenticationAsync(HttpClient client, string serviceName, CancellationToken cancellationToken)
    {
        var options = _options.CurrentValue;
        var authOptions = options.Authentication;

        if (authOptions.Type == AuthenticationType.None)
        {
            return;
        }

        try
        {
            var token = await _authenticationProvider.GetAccessTokenAsync(serviceName, cancellationToken);
            if (!string.IsNullOrEmpty(token))
            {
                client.DefaultRequestHeaders.Authorization =
                    new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);

                _logger.LogDebug("为服务 {ServiceName} 添加认证Token", serviceName);
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "为服务 {ServiceName} 获取认证Token失败", serviceName);
        }
    }

    private static void AddUserContextHeaders(HttpClient client, UserContext userContext)
    {
        if (!string.IsNullOrEmpty(userContext.UserId))
        {
            client.DefaultRequestHeaders.Add("X-User-Id", userContext.UserId);
        }

        if (!string.IsNullOrEmpty(userContext.UserName))
        {
            client.DefaultRequestHeaders.Add("X-User-Name", userContext.UserName);
        }

        if (!string.IsNullOrEmpty(userContext.TenantId))
        {
            client.DefaultRequestHeaders.Add("X-Tenant-Id", userContext.TenantId);
        }

        if (!string.IsNullOrEmpty(userContext.RequestId))
        {
            client.DefaultRequestHeaders.Add("X-Request-Id", userContext.RequestId);
        }

        if (!string.IsNullOrEmpty(userContext.TraceId))
        {
            client.DefaultRequestHeaders.Add("X-Trace-Id", userContext.TraceId);
        }

        // 添加自定义属性
        foreach (var property in userContext.Properties.Where(p => p.Value != null))
        {
            client.DefaultRequestHeaders.Add($"X-Custom-{property.Key}", property.Value.ToString()!);
        }
    }
}